package pub.pjoc.pay.threadpool.asynchronous;

import java.math.BigDecimal;

/**
 * Monitor unit.
 * @author blademainer
 * @version 2016-08-31 23:16
 */
public class MonitorUnit {
  private Class<?> clazz;
  private BigDecimal averageTime = new BigDecimal(0);
  private BigDecimal count = new BigDecimal(0);
  private BigDecimal successAverageTime = new BigDecimal(0);
  private BigDecimal successCount = new BigDecimal(0);
  private BigDecimal failedAverageTime = new BigDecimal(0);
  private BigDecimal failedCount = new BigDecimal(0);
  private BigDecimal timeoutCount = new BigDecimal(0);
  private BigDecimal timeOutAverageTime = new BigDecimal(0);

  public MonitorUnit(Class<?> clazz) {
    this.clazz = clazz;
  }

  public void success(long time) {
    BigDecimal[] bigDecimals = calculateAverage(successAverageTime, successCount, time);
    successAverageTime = bigDecimals[0];
    successCount = bigDecimals[1];

    invoked(time);
  }

  public void fail(long time) {
    BigDecimal[] bigDecimals = calculateAverage(failedAverageTime, failedCount, time);
    failedAverageTime = bigDecimals[0];
    failedCount = bigDecimals[1];

    invoked(time);
  }

  public void timeout(long time) {
    BigDecimal[] bigDecimals = calculateAverage(timeOutAverageTime, timeoutCount, time);
    timeOutAverageTime = bigDecimals[0];
    timeoutCount = bigDecimals[1];

    invoked(time);
  }

  public void invoked(long time) {
    BigDecimal[] bigDecimals = calculateAverage(averageTime, count, time);
    averageTime = bigDecimals[0];
    count = bigDecimals[1];
  }

  /**
   * 计算平均值.
   *
   * @param averageTime 当前平均值
   * @param count       当前总数
   * @param newTime     新的时间
   * @return 返回averageTime和count
   */
  public static BigDecimal[] calculateAverage(BigDecimal averageTime, BigDecimal count,
                                              long newTime) {
    BigDecimal[] bigDecimals = new BigDecimal[2];
    BigDecimal sumDecimal = averageTime.multiply(count);
    count = count.add(new BigDecimal(1));
    bigDecimals[1] = count;
    BigDecimal timeDecimal = new BigDecimal(newTime);
    sumDecimal = sumDecimal.add(timeDecimal);

    averageTime = sumDecimal.divide(count, 2, BigDecimal.ROUND_HALF_UP);
    bigDecimals[0] = averageTime;
    return bigDecimals;
  }

  /**
   * 四舍五入除法.
   *
   * @param dividend 被除数
   * @param divisor  除数
   * @return
   */
  public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor) {
    BigDecimal result = dividend.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
    return result;
  }

  public Class<?> getClazz() {
    return clazz;
  }

  public void setClazz(Class<?> clazz) {
    this.clazz = clazz;
  }

  public BigDecimal getAverageTime() {
    return averageTime;
  }

  public void setAverageTime(BigDecimal averageTime) {
    this.averageTime = averageTime;
  }

  public BigDecimal getCount() {
    return count;
  }

  public void setCount(BigDecimal count) {
    this.count = count;
  }

  public BigDecimal getSuccessAverageTime() {
    return successAverageTime;
  }

  public void setSuccessAverageTime(BigDecimal successAverageTime) {
    this.successAverageTime = successAverageTime;
  }

  public BigDecimal getSuccessCount() {
    return successCount;
  }

  public void setSuccessCount(BigDecimal successCount) {
    this.successCount = successCount;
  }

  public BigDecimal getFailedAverageTime() {
    return failedAverageTime;
  }

  public void setFailedAverageTime(BigDecimal failedAverageTime) {
    this.failedAverageTime = failedAverageTime;
  }

  public BigDecimal getFailedCount() {
    return failedCount;
  }

  public void setFailedCount(BigDecimal failedCount) {
    this.failedCount = failedCount;
  }

  public BigDecimal getTimeoutCount() {
    return timeoutCount;
  }

  public void setTimeoutCount(BigDecimal timeoutCount) {
    this.timeoutCount = timeoutCount;
  }

  public BigDecimal getTimeOutAverageTime() {
    return timeOutAverageTime;
  }

  public void setTimeOutAverageTime(BigDecimal timeOutAverageTime) {
    this.timeOutAverageTime = timeOutAverageTime;
  }

  @Override
  public String toString() {
    return "MonitorUnit{" +
        "clazz=" + clazz +
        ", averageTime=" + averageTime +
        ", count=" + count +
        ", successAverageTime=" + successAverageTime +
        ", successCount=" + successCount +
        ", failedAverageTime=" + failedAverageTime +
        ", failedCount=" + failedCount +
        ", timeoutCount=" + timeoutCount +
        ", timeOutAverageTime=" + timeOutAverageTime +
        '}';
  }
}
